/**
 * Project  : Looker
 * Package  : net.freestu.looker.monitor
 * File     : NewsMetaListManager.java
 * Author   : solosky < solosky772@qq.com >
 * Date     : Aug 26, 2009
 * Modified : Aug 26, 2009
 * License  : Apache License 2.0 
 */
package net.freestu.looker.monitor;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

import net.freestu.looker.common.ConfigurationManager;
import net.freestu.looker.common.Looker;
import net.freestu.looker.common.NewsEntity;
import net.freestu.looker.common.NewsMeta;

import org.apache.log4j.Logger;

/**
 *  新闻临时列表管理
 *  为了减少内存使用，每一次检查结束后会把列表保存在一个临时文件中
 *  文件位置由looker.tmpListDir指定，命名方式为[栏目的hash].list
 *  这个类管理了临时列表读取保存和更新操作
 *  并完成新闻的检查和新增消息的处理
 */
public class NewsMetaListManager
{

	/**
	 * 临时新闻列表文件名
	 */
	private String listFileName;
	
	
	/**
	 * 之前的新闻列表
	 */
	private ArrayList<NewsMeta> beforeList;
	
	/**
	 * 现在的新闻列表
	 */
	private ArrayList<NewsMeta> currentList;
	
	/**
	 * 新闻检查器
	 */
	private ModifyChecker modifyChecker;
	
	/**
	 * 日志记录
	 */
	private static Logger logger = Logger.getLogger(NewsMetaListManager.class);
	
	/**
	 * 构造函数，参数为唯一的栏目编号
	 */
	public NewsMetaListManager(String cateHash)
	{
		this.listFileName = ConfigurationManager.getInstance()
							.getLookerConfiguration()
							.getString("looker.tmpListDir")+cateHash+".list"; 
		this.beforeList = new ArrayList<NewsMeta>();
	}
	
	/**
	 * 从临时文件中读取之前的新闻列表，并保存到beforeList
	 * @throws IOException 
	 */
	public void readBeforeList() throws IOException
	{
		this.beforeList.clear();
		this.readList(this.beforeList);
		logger.debug("成功读取临时新闻列表文件 ["+this.listFileName+"]");
	}
	
	/**
	 * 保存当前新闻列表至临时文件
	 * @throws IOException 
	 */
	public void saveCurrentList() throws IOException
	{
		this.saveList(this.currentList);
	}
	
	/**
	 * 设置当前新闻列表
	 * @param currentList
	 */
	public void setCurrentList(ArrayList<NewsMeta> currentList)
	{
		this.currentList = currentList;
	}
	
	
	
	public ArrayList<NewsMeta> getCurrentList()
    {
    	return currentList;
    }

	public void setBeforeList(ArrayList<NewsMeta> beforeList)
    {
    	this.beforeList = beforeList;
    }

	/**
	 * 检查新闻更改
	 */
	public void checkModify()
	{
		modifyChecker = new ModifyChecker(beforeList, currentList);
		modifyChecker.checkModify();
		ArrayList<NewsMeta> tmpList  = null;
		Iterator<NewsMeta> iterator  = null;
		ModifyDispatcher  dispatcher = ModifyDispatcher.getInstance();  
		//新增
		tmpList = modifyChecker.getAddedNewsList();
		if(tmpList!=null && tmpList.size()>0) {
			iterator = tmpList.iterator();
			while(iterator.hasNext()) {
				NewsMeta added = iterator.next();
				logger.info("添加新闻："+added.getUrl()+" - "+added.getCategory().getTitle());
				//下载新闻正文和附件
				 NewsEntity newsEntity = this.archiveNewsEntity(added);
				 if(newsEntity!=null) {
    		        //更新新闻临时列表
    		        updateNewsMeta(newsEntity.toNewsMeta());
    		        //交给分发器
    		        dispatcher.newsAdded(newsEntity);
				 }
			}
		}
		//删除
		tmpList = modifyChecker.getDeletedNewsList();
		if(tmpList!=null && tmpList.size()>0) {
			iterator = tmpList.iterator();
			while(iterator.hasNext())
				dispatcher.newsDeleted(iterator.next());
		}
		//更新
		tmpList = modifyChecker.getUpdatedNewsList();
		if(tmpList!=null && tmpList.size()>0) {
			iterator = tmpList.iterator();
			while(iterator.hasNext()) {
				//下载新闻正文和附件
				 NewsEntity newsEntity = this.archiveNewsEntity(iterator.next());
				 if(newsEntity!=null) {
       		        //更新新闻临时列表
       		        updateNewsMeta(newsEntity.toNewsMeta());
       		        //交给分发器
       		        dispatcher.newsUpdated(newsEntity);
				 }
			}
		}
	}
	
	/**
	 * 更新新闻
	 * 主要是更新新闻正文的签名和附件列表
	 */
	public void updateNewsMeta(NewsMeta newsMeta)
	{
		//遍历需要更新的列表，更新新闻信息
		Iterator<NewsMeta>it = currentList.iterator();
		NewsMeta tmpNewsMeta = null;
		while(it.hasNext()) {
			tmpNewsMeta = it.next();
			if(tmpNewsMeta.getHash().equals(newsMeta.getHash())) {
				//更新新闻正文签名和附件
				tmpNewsMeta.setSignature(newsMeta.getSignature());
				tmpNewsMeta.setMediaHashList(newsMeta.getMediaHashList());
				break;
			}
		}
		if(!it.hasNext())
			logger.warn("需更新的新闻 ["+newsMeta.getUrl()+"] 在原列表中不存在。");		
	}
	
	/**
	 * 返回以前的新闻列表
	 * @return
	 */
	public ArrayList<NewsMeta> getBeforeList()
	{
		return this.beforeList;
	}
	
	/**
	 * 提取新闻正文
	 */
	public NewsEntity archiveNewsEntity(NewsMeta newsMeta)
	{
		//下载新闻和附件
		NewsArchiver archiver = new NewsArchiver(newsMeta);
		Future<NewsEntity> future = Looker.executors.submit(archiver);
		NewsEntity newsEntity = null;
		try {
	       newsEntity = future.get();
        } catch (InterruptedException e) {
        	logger.warn("等待NewsArchiver完成时线程发生中断 : "+e.getMessage());
        } catch (ExecutionException e) {
			logger.warn("等待NewsArchiver完成时线程池出现错误 : "+e.getCause().getMessage());
        }
        return newsEntity;
	}
	
	/**
	 * 因为分析的列表中并不包含正文签名数据和附件数据，
	 * 这里需要合并以前的正文签名数据或者附件数据
	 */
	public void updateCurrentList(boolean isUpdateSignature, boolean isUpdateMediaList)
	{
		//以前的列表建立hash，便于查找
		HashMap<String, NewsMeta> map = new HashMap<String, NewsMeta>();
		Iterator<NewsMeta> it = beforeList.iterator();
		NewsMeta tmpNewsMeta = null;
		NewsMeta oldNewsMeta = null;
		while (it.hasNext()) {
			tmpNewsMeta = it.next();
			map.put(tmpNewsMeta.getHash(),tmpNewsMeta);
		}
		
		//遍历最新的新闻列表，更新正文签名或者附件数据
		it = currentList.iterator();
		while(it.hasNext()) {
			tmpNewsMeta = it.next();
			if(map.containsKey(tmpNewsMeta.getHash())) {
				oldNewsMeta = map.get(tmpNewsMeta.getHash());
				if(isUpdateSignature)
					tmpNewsMeta.setSignature(oldNewsMeta.getSignature());
				if(isUpdateMediaList)
					tmpNewsMeta.setMediaHashList(oldNewsMeta.getMediaHashList());
			}
		}
	}
	
	/**
	 * 保存一个新闻列表
	 * @param list
	 * @throws IOException 
	 */
	private void saveList(ArrayList<NewsMeta> list) throws IOException
	{
		FileWriter writer     = new FileWriter(new File(this.listFileName));
		Iterator<NewsMeta> it = list.iterator();
		while(it.hasNext()) {
			writer.append(this.newsMeta2Line(it.next()));
		}
		//关闭文件
		writer.close();
	}
	
	/**
	 * 从临时新闻列表文件中读取记录至给定的列表
	 * @param list
	 * @throws IOException 
	 */
	private void readList(ArrayList<NewsMeta> list) throws IOException
	{
		File listFile = new File(this.listFileName);
		if(!listFile.exists()) {
			listFile.createNewFile();
			logger.debug("新闻临时列表文件[ "+this.listFileName+" ]不存在，将自动创建。");
		}
		if(list.size()>0)
			list.clear();
		//读取每行记录并转变为一个NewsMeta对象
		BufferedReader reader = new BufferedReader(new FileReader(listFile));
		String line       = null;
		while( (line=reader.readLine())!=null) {
			list.add(this.line2NewsMeta(line));
		}
	}
	
	/**
	 * 一行记录格式化为一个NewsMeta对象
	 */
	private NewsMeta line2NewsMeta(String line)
	{
		String[] tokens = line.split("\\|");
		NewsMeta newsMeta = new NewsMeta();
		newsMeta.setUrl(tokens[0]);			//URL 地址
		newsMeta.setSignature(tokens.length>1?tokens[1]:null);	//正文签名
											//附件列表
		if(tokens.length>2) {
    		String [] medias  = tokens[2].split("/");
    		ArrayList<String> mediaHashList = new ArrayList<String>();
    		for(int i=0;i<medias.length;i++) {
    			mediaHashList.add(medias[i]);
    		}
    		newsMeta.setMediaHashList(mediaHashList);
		}
		return newsMeta;
	
	}
	
	/**
	 * 一个newsMeta格式化为一行记录
	 */
	private String newsMeta2Line(NewsMeta newsMeta)
	{
		StringBuffer buffer = new StringBuffer();
		buffer.append(newsMeta.getUrl());		//新闻URL
		buffer.append('|');
		buffer.append(newsMeta.getSignature()==null?"":newsMeta.getSignature());	//新闻正文标记
		buffer.append('|');
		ArrayList<String> medias = newsMeta.getMediaHashList();	//新闻附件编号列表
		if(medias !=null && medias.size()>0) {
    		for(int i=0;i<medias.size(); i++) {
    			if(i!=0)
    				buffer.append('/');
    			buffer.append(medias.get(i));	
    		}
		}
		buffer.append('\n');
		return buffer.toString();
	}
	
}
